package cn.lbd;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class ChannelDemo {
    //利用通道完成文件的复制（非直接缓冲区）
    @Test
    public void test1() {
        long start = System.currentTimeMillis();
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inputStream = new FileInputStream("E:\\IO\\demo1\\rinv.jpg");
            outputStream = new FileOutputStream("E:\\IO\\demo2\\rinv.jpg");
            //Java 针对支持通道的流提供了 getChannel() 方法
            inChannel = inputStream.getChannel();
            outChannel = outputStream.getChannel();
            //通道要通过缓冲区传递数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //将通道中的数据读入如缓冲区
            while (inChannel.read(buffer) != -1) {
                buffer.flip();
                //将缓冲区的数据写入通道
                outChannel.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
                if (inChannel != null) {
                    inChannel.close();
                }
                if (outChannel != null) {
                    outChannel.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("执行时间：" + (end - start) + "ms");
    }


    //使用直接缓冲区完成文件的复制(内存映射文件)
    @Test
    public void test2() {
        long start = System.currentTimeMillis();
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            //针对各个通道提供了静态方法 open()
            inChannel = FileChannel.open(Paths.get("E:\\IO\\demo1\\rinv.jpg"), StandardOpenOption.READ);
            outChannel = FileChannel.open(Paths.get("E:\\IO\\demo2\\rinv.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

            //内存映射文件，直接缓冲区，直接映射到物理内存上，省略了内核地址空间到用户内核空间的copy操作
            MappedByteBuffer inMapBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMapBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
            byte[] bytes = new byte[inMapBuffer.limit()];
            //读
            inMapBuffer.get(bytes);
            //写
            outMapBuffer.put(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inChannel != null) {
                    inChannel.close();
                }
                if (outChannel != null) {
                    outChannel.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("执行时间：" + (end - start) + "ms");
    }

    //通道之间的数据传输(直接缓冲区) ----简单易行 推荐！
    @Test
    public void test3() {
        long start = System.currentTimeMillis();
        try {
            FileChannel inChannel = FileChannel.open(Paths.get("E:\\IO\\demo1\\rinv.jpg"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("E:\\IO\\demo2\\rinv.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
            inChannel.transferTo(0, inChannel.size(), outChannel);
            if (inChannel != null) {
                inChannel.close();
            }
            if (outChannel != null) {
                outChannel.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("执行时间：" + (end - start) + "ms");
    }

    //通道之间的数据传输(直接缓冲区) ----简单易行 推荐！
    @Test
    public void test4() {
        long start = System.currentTimeMillis();
        try {
            FileChannel inChannel = FileChannel.open(Paths.get("E:\\IO\\demo1\\rinv.jpg"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("E:\\IO\\demo2\\rinv.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
            outChannel.transferFrom(inChannel,0,inChannel.size());
            if (inChannel != null) {
                inChannel.close();
            }
            if (outChannel != null) {
                outChannel.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("执行时间：" + (end - start) + "ms");
    }

    @Test
    public void test5() {
        try {
            RandomAccessFile raf1 = new RandomAccessFile("E:\\IO\\demo1\\1.txt","r");
            FileChannel channel = raf1.getChannel();
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(100);
            ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024);
            //分散读取到不同的缓冲区中
            ByteBuffer[] byteBuffers = new ByteBuffer[]{byteBuffer1,byteBuffer2};
            channel.read(byteBuffers);
            for (ByteBuffer byteBuffer : byteBuffers) {
                byteBuffer.flip();
            }
            //从position到limit是有效长度
            System.out.println(new String(byteBuffers[0].array(),byteBuffers[0].position(),byteBuffers[0].limit()));
            System.out.println(new String(byteBuffers[1].array(),byteBuffers[1].position(),byteBuffers[1].limit()));
            RandomAccessFile raf2 = new RandomAccessFile("E:\\IO\\demo2\\1.txt","rw");
            FileChannel channel1 = raf2.getChannel();
            //将所有缓冲区汇总成一个数组，聚集的写入到通道中
            channel1.write(byteBuffers);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //查看有多少字符集
    @Test
    public void test6(){
        Map<String, Charset> map = Charset.availableCharsets();

        Set<Map.Entry<String, Charset>> set = map.entrySet();

        for (Map.Entry<String, Charset> entry : set) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }

    //字符集编码和解码
    @Test
    public void test7() throws CharacterCodingException {
        //设置GBK编码字符集
        Charset charset = Charset.forName("GBK");
        //编码器
        CharsetEncoder charsetEncoder = charset.newEncoder();
        //解码器
        CharsetDecoder charsetDecoder = charset.newDecoder();
        CharBuffer charBuffer =  CharBuffer.allocate(1024);
        charBuffer.put("PHP是世界上最好的语言");
        charBuffer.flip();
        ByteBuffer encode = charsetEncoder.encode(charBuffer);
        for (int i = 0; i < encode.limit(); i++) {
            System.out.println(encode.get());
        }
        encode.flip();
        CharBuffer decode = charsetDecoder.decode(encode);
        System.out.println(decode.toString());

        Charset charset1 = Charset.forName("UTF-8");
        encode.flip();
        CharBuffer decode1 = charset1.decode(encode);
        System.out.println(decode1.toString());
    }
}
